
/******************************************************************************
 * \file     startup_evalsoc.S
 * \brief    NMSIS Nuclei N/NX Class Core Startup File for N200.
 * \version  V1.00
 * \date     17. Dec 2019
 ******************************************************************************/
 
 
#include "riscv_encoding.h"



.section .text.vtable

    .globl vector_base
    .type vector_base, @object
    .option push
    .option norelax

vector_base:
	j 	_start                     				/* 0: Reserved, Jump to _start when reset for vector table not remapped cases.*/
	
	.word	default_intexc_handler				/* 1: Reserved */
	.word	default_intexc_handler				/* 2: Reserved */
	.word	eclic_msip_handler              	/* 3: Machine software interrupt */
	
	.word	default_intexc_handler				/* 4: Reserved */
	.word	default_intexc_handler				/* 5: Reserved */
	.word	default_intexc_handler				/* 6: Reserved */
	.word	eclic_mtip_handler					/* 7: Machine timer interrupt */
	
	.word	default_intexc_handler				/* 8: Reserved */
	.word	default_intexc_handler				/* 9: Reserved */
	.word	default_intexc_handler				/* 10: Reserved */
	.word	default_intexc_handler				/* 11: Reserved */
	
	.word	default_intexc_handler				/* 12: Reserved */
	.word	default_intexc_handler				/* 13: Reserved */
	.word	default_intexc_handler				/* 14: Reserved */
	.word	default_intexc_handler				/* 15: Reserved */
	
	.word	eclic_inter_core_int_handler		/* 16: Reserved */
	.word	default_intexc_handler				/* 17: Reserved */
	.word	default_intexc_handler				/* 18: Reserved */
	
	.word	default_intexc_handler				/* 19: Extern Interrupt */
	.word	default_intexc_handler				/* 20: Extern Interrupt */
	.word	default_intexc_handler				/* 21: Extern Interrupt */
	.word	default_intexc_handler				/* 22: Extern Interrupt */
	.word	default_intexc_handler				/* 23: Extern Interrupt */

	.word	default_intexc_handler				/* 20: Extern Interrupt */
	.word	default_intexc_handler				/* 21: Extern Interrupt */
	.word	default_intexc_handler				/* 22: Extern Interrupt */
	.word	default_intexc_handler				/* 23: Extern Interrupt */
		
	
    .option pop


.section .text.init
.globl _start
.type _start, @function

	
	/** Reset Handler called on controller reset */
_start:
    /* ===== Startup Stage 1 ===== */
    csrc CSR_MSTATUS, MSTATUS_MIE				/* Disable Global Interrupt */
    
    /* Initialize GP and TP and jump table base when zcmt enabled */
    .option push
    .option norelax
    la gp, __global_pointer$
    la tp, __tls_base
#if defined(__riscv_zcmt)
    la t0, __jvt_base$
    csrw CSR_JVT, t0
#endif
    .option pop

    /* wmd : Set correct sp for current cpu,  DLM : 0x90000000 - 0x9000FFFF */
    lui		sp, 0x90010
    li      t0, 1
    csrs    CSR_MDLM_CTL, t0
    
    /*
     * Set the the NMI base mnvec to share
     * with mtvec by setting CSR_MMISC_CTL
     * bit 9 NMI_CAUSE_FFF to 1
     */
    li 		t0, MMISC_CTL_NMI_CAUSE_FFF
    csrs 	CSR_MMISC_CTL, t0

    /*
     * Enable Zc feature when compiled zcmp & zcmt
     */
#if defined(__riscv_zcmp) || defined(__riscv_zcmt)
    li 		t0, MMISC_CTL_ZC
    csrs 	CSR_MMISC_CTL, t0
#endif
	
    /*
     * Intialize ECLIC vector interrupt
     * base address mtvt to vector_base
     */
    la 		t0, vector_base
    csrw 	CSR_MTVT, t0

    /*
     * Set ECLIC non-vector entry to be controlled
     * by mtvt2 CSR register.
     * Intialize ECLIC non-vector interrupt
     * base address mtvt2 to irq_entry.
     */
    la 		t0, irq_entry
    csrw 	CSR_MTVT2, t0
    csrs 	CSR_MTVT2, 0x1

    /*
     * Set Exception Entry MTVEC to early_exc_entry
     * Due to settings above, Exception and NMI
     * will share common entry.
     * This early_exc_entry is only used during early
     * boot stage before main
     */
    la 		t0, early_exc_entry
    csrw 	CSR_MTVEC, t0

    /* Set the interrupt processing mode to ECLIC mode */
    li 		t0, 0x3f
    csrc 	CSR_MTVEC, t0
    csrs 	CSR_MTVEC, 0x3

    /* ===== Startup Stage 2 ===== */

    /* Enable FPU and Vector Unit if f/d/v exist in march */
#if defined(__riscv_flen) && __riscv_flen > 0
    /* Enable FPU, and set state to initial */
    li t0, MSTATUS_FS
    csrc mstatus, t0
    li t0, MSTATUS_FS_INITIAL
    csrs mstatus, t0
#endif

#if defined(__riscv_vector)
    /* Enable Vector, and set state to initial */
    li t0, MSTATUS_VS
    csrc mstatus, t0
    li t0, MSTATUS_VS_INITIAL
    csrs mstatus, t0
#endif

    /* Enable mcycle and minstret counter */
    csrci 	CSR_MCOUNTINHIBIT, 0x5


__init_common:
    /* ===== Startup Stage 3 ===== */

    /* Load data section */
    la 		a0, _data_lma
    la 		a1, _data
    beq 	a0, a1, 2f				/* If data vma=lma, no need to copy */
    la 		a2, _edata
    bgeu 	a1, a2, 2f
1:
    lw 		t0, (a0)
    sw 		t0, (a1)
    addi 	a0, a0, 4
    addi 	a1, a1, 4
    bltu 	a1, a2, 1b
    
2:
    /* Clear bss section */
    la a0, __bss_start
    la a1, _end
    bgeu a0, a1, 2f
1:
    sw zero, (a0)
    addi a0, a0, 4
    bltu a0, a1, 1b
2:

.globl _start_premain
.type _start_premain, @function
_start_premain:
    /*
     * Call vendor defined SystemInit to
     * initialize the micro-controller system
     * SystemInit will just be called by boot cpu
     */
    call SystemInit

    /*
     * Call C/C++ constructor start up code,
     * __libc_fini is defined in linker script,
     * so register_fini function will be called
     * and will run atexit (__libc_fini_array)
     * to do previous call atexit function
     */
    call __libc_init_array

__skip_init:

    /* do pre-init steps before main */
    /* _premain_init will be called by each cpu
     * please make sure the implementation of __premain_int
     * considered this
     */
    call _premain_init

    /*
     * When all initialization steps done
     * set exception entry to correct exception
     * entry and jump to main.
     * And set the interrupt processing mode to
     * ECLIC mode
     */
    la t0, exc_entry
    csrw CSR_MTVEC, t0
    li t0, 0x3f
    csrc CSR_MTVEC, t0
    csrs CSR_MTVEC, 0x3

    /* BPU cold bringup need time, so enable BPU before enter to main */
    li t0, MMISC_CTL_BPU
    csrs CSR_MMISC_CTL, t0

    /* ===== Call Main Function  ===== */
    /* argc = argv = 0 */
    li a0, 0
    li a1, 0
    call main
    
    /* do post-main steps after main
     * this function will be called by each cpu */
    call _postmain_fini





/* Early boot exception entry before main */
.align 6
.global early_exc_entry
.type early_exc_entry, @function

early_exc_entry:
    wfi
    j early_exc_entry




/**
 * \brief  Global interrupt disabled
 * \details
 *  This function disable global interrupt.
 * \remarks
 *  - All the interrupt requests will be ignored by CPU.
 */
.macro DISABLE_MIE
    csrc CSR_MSTATUS, MSTATUS_MIE
.endm

/**
 * \brief  Macro for context save
 * \details
 * This macro save ABI defined caller saved registers in the stack.
 * \remarks
 * - This Macro could use to save context when you enter to interrupt
 * or exception
*/
/* Save caller registers */
.macro SAVE_CONTEXT
    /* Allocate stack space for context saving */
#ifndef __riscv_32e
    addi sp, sp, -20*REGBYTES
#else
    addi sp, sp, -14*REGBYTES
#endif /* __riscv_32e */

    STORE x1, 0*REGBYTES(sp)
    STORE x4, 1*REGBYTES(sp)
    STORE x5, 2*REGBYTES(sp)
    STORE x6, 3*REGBYTES(sp)
    STORE x7, 4*REGBYTES(sp)
    STORE x10, 5*REGBYTES(sp)
    STORE x11, 6*REGBYTES(sp)
    STORE x12, 7*REGBYTES(sp)
    STORE x13, 8*REGBYTES(sp)
    STORE x14, 9*REGBYTES(sp)
    STORE x15, 10*REGBYTES(sp)
#ifndef __riscv_32e
    STORE x16, 14*REGBYTES(sp)
    STORE x17, 15*REGBYTES(sp)
    STORE x28, 16*REGBYTES(sp)
    STORE x29, 17*REGBYTES(sp)
    STORE x30, 18*REGBYTES(sp)
    STORE x31, 19*REGBYTES(sp)
#endif /* __riscv_32e */
.endm

/**
 * \brief  Macro for restore caller registers
 * \details
 * This macro restore ABI defined caller saved registers from stack.
 * \remarks
 * - You could use this macro to restore context before you want return
 * from interrupt or exeception
 */
/* Restore caller registers */
.macro RESTORE_CONTEXT
    LOAD x1, 0*REGBYTES(sp)
    LOAD x4, 1*REGBYTES(sp)
    LOAD x5, 2*REGBYTES(sp)
    LOAD x6, 3*REGBYTES(sp)
    LOAD x7, 4*REGBYTES(sp)
    LOAD x10, 5*REGBYTES(sp)
    LOAD x11, 6*REGBYTES(sp)
    LOAD x12, 7*REGBYTES(sp)
    LOAD x13, 8*REGBYTES(sp)
    LOAD x14, 9*REGBYTES(sp)
    LOAD x15, 10*REGBYTES(sp)
#ifndef __riscv_32e
    LOAD x16, 14*REGBYTES(sp)
    LOAD x17, 15*REGBYTES(sp)
    LOAD x28, 16*REGBYTES(sp)
    LOAD x29, 17*REGBYTES(sp)
    LOAD x30, 18*REGBYTES(sp)
    LOAD x31, 19*REGBYTES(sp)

    /* De-allocate the stack space */
    addi sp, sp, 20*REGBYTES
#else
    /* De-allocate the stack space */
    addi sp, sp, 14*REGBYTES
#endif /* __riscv_32e */

.endm

/**
 * \brief  Macro for save necessary CSRs to stack
 * \details
 * This macro store MCAUSE, MEPC, MSUBM to stack.
 */
.macro SAVE_CSR_CONTEXT
    /* Store CSR mcause to stack using pushmcause */
    csrrwi  x0, CSR_PUSHMCAUSE, 11
    /* Store CSR mepc to stack using pushmepc */
    csrrwi  x0, CSR_PUSHMEPC, 12
    /* Store CSR msub to stack using pushmsub */
    csrrwi  x0, CSR_PUSHMSUBM, 13
.endm

/**
 * \brief  Macro for restore necessary CSRs from stack
 * \details
 * This macro restore MSUBM, MEPC, MCAUSE from stack.
 */
.macro RESTORE_CSR_CONTEXT
    LOAD x5,  13*REGBYTES(sp)
    csrw CSR_MSUBM, x5
    LOAD x5,  12*REGBYTES(sp)
    csrw CSR_MEPC, x5
    LOAD x5,  11*REGBYTES(sp)
    csrw CSR_MCAUSE, x5
.endm

/**
 * \brief  Exception/NMI Entry
 * \details
 * This function provide common entry functions for exception/nmi.
 * \remarks
 * This function provide a default exception/nmi entry.
 * ABI defined caller save register and some CSR registers
 * to be saved before enter interrupt handler and be restored before return.
 */
.section .text.trap
/* In CLIC mode, the exeception entry must be 64bytes aligned */
.align 6
# gnu let .weak override .globl, but llvm will show warning
# see https://reviews.llvm.org/D90108
.weak exc_entry
exc_entry:
    /* Save the caller saving registers (context) */
    SAVE_CONTEXT
    /* Save the necessary CSR registers */
    SAVE_CSR_CONTEXT

    /*
     * Set the exception handler function arguments
     * argument 1: mcause value
     * argument 2: current stack point(SP) value
     */
    csrr a0, mcause
    mv a1, sp
    /*
     * TODO: Call the exception handler function
     * By default, the function template is provided in
     * system_Device.c, you can adjust it as you want
     */
    call core_exception_handler

    /* Restore the necessary CSR registers */
    RESTORE_CSR_CONTEXT
    /* Restore the caller saving registers (context) */
    RESTORE_CONTEXT

    /* Return to regular code */
    mret




/**
 * \brief  Non-Vector Interrupt Entry
 * \details
 * This function provide common entry functions for handling
 * non-vector interrupts
 * \remarks
 * This function provide a default non-vector interrupt entry.
 * ABI defined caller save register and some CSR registers need
 * to be saved before enter interrupt handler and be restored before return.
 */
.section      .text.irq
/* In CLIC mode, the interrupt entry must be 4bytes aligned */
.align 2
# gnu let .weak override .globl, but llvm will show warning
# see https://reviews.llvm.org/D90108
.weak irq_entry
/* This label will be set to MTVT2 register */
irq_entry:
    /* Save the caller saving registers (context) */
    SAVE_CONTEXT
    /* Save the necessary CSR registers */
    SAVE_CSR_CONTEXT

    /* This special CSR read/write operation, which is actually
     * claim the CLIC to find its pending highest ID, if the ID
     * is not 0, then automatically enable the mstatus.MIE, and
     * jump to its vector-entry-label, and update the link register
     */
    csrrw ra, CSR_JALMNXTI, ra

    /* Critical section with interrupts disabled */
    DISABLE_MIE

    /* Restore the necessary CSR registers */
    RESTORE_CSR_CONTEXT
    /* Restore the caller saving registers (context) */
    RESTORE_CONTEXT

    /* Return to regular code */
    mret



/* Default Handler for Exceptions / Interrupts */
# gnu let .weak override .globl, but llvm will show warning
# see https://reviews.llvm.org/D90108
.weak default_intexc_handler

eclic_msip_handler:
eclic_mtip_handler:
eclic_inter_core_int_handler:
default_intexc_handler:
1:
    j 1b



